package com.ala4.oxcafe.boot.authentication;

import com.ala4.oxcafe.boot.handler.AuthenticationFailureEntryPoint;
import com.ala4.oxcafe.boot.manager.UserTokenManager;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationManagerResolver;
import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint;
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
import org.springframework.security.oauth2.server.resource.web.DefaultBearerTokenResolver;
import org.springframework.security.oauth2.server.resource.web.authentication.BearerTokenAuthenticationFilter;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.util.matcher.RequestMatcher;
import org.springframework.util.Assert;

/**
 * 鉴权配置
 *
 * @author PING
 * @version 1.0
 * @date 2025/3/4 17:51
 */
public class CustomAuthenticationConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractHttpConfigurer<CustomAuthenticationConfigurer<H>, H> {

    private final UserTokenManager userTokenManager;

    private AuthenticationManagerResolver<HttpServletRequest> authenticationManagerResolver;

    private BearerTokenResolver bearerTokenResolver;

    private BearerTokenRequestMatcher requestMatcher = new BearerTokenRequestMatcher();

    // 认证失败处理器
    private AuthenticationEntryPoint authenticationEntryPoint = new BearerTokenAuthenticationEntryPoint();

    public CustomAuthenticationConfigurer(UserTokenManager userTokenManager) {
        Assert.notNull(userTokenManager, "userTokenManager cannot be null");
        this.userTokenManager = userTokenManager;
    }

    @Override
    public void init(H http) {

        registerDefaultCsrfOverride(http);

        http.authenticationProvider(new CustomAuthenticationProvider(userTokenManager));
    }

    /**
     * 配置一个过滤器
     *
     * @param http http
     */
    @Override
    public void configure(H http) {
        BearerTokenResolver bearerTokenResolver = getBearerTokenResolver();
        this.requestMatcher.setBearerTokenResolver(bearerTokenResolver);
        AuthenticationManagerResolver resolver = this.authenticationManagerResolver;
        if (resolver == null) {
            AuthenticationManager authenticationManager = getAuthenticationManager(http);
            resolver = (request) -> authenticationManager;
        }

        BearerTokenAuthenticationFilter filter = new BearerTokenAuthenticationFilter(resolver);
        filter.setBearerTokenResolver(bearerTokenResolver);
        filter.setAuthenticationEntryPoint(AuthenticationFailureEntryPoint::exceptionHandler);
        filter.setSecurityContextHolderStrategy(getSecurityContextHolderStrategy());
        filter = postProcess(filter);
        http.addFilter(filter);
    }

    private void registerDefaultCsrfOverride(H http) {
        CsrfConfigurer<H> csrf = http.getConfigurer(CsrfConfigurer.class);
        if (csrf != null) {
            csrf.ignoringRequestMatchers(this.requestMatcher);
        }
    }

    BearerTokenResolver getBearerTokenResolver() {
        return new DefaultBearerTokenResolver();
    }


    public CustomAuthenticationConfigurer<H> bearerTokenResolver(BearerTokenResolver bearerTokenResolver) {
        Assert.notNull(bearerTokenResolver, "bearerTokenResolver cannot be null");
        this.bearerTokenResolver = bearerTokenResolver;
        return this;
    }

    AuthenticationManager getAuthenticationManager(H http) {
        return http.getSharedObject(AuthenticationManager.class);
    }


    private static final class BearerTokenRequestMatcher implements RequestMatcher {

        private BearerTokenResolver bearerTokenResolver;

        @Override
        public boolean matches(HttpServletRequest request) {
            try {
                return this.bearerTokenResolver.resolve(request) != null;
            } catch (OAuth2AuthenticationException ex) {
                return false;
            }
        }

        void setBearerTokenResolver(BearerTokenResolver tokenResolver) {
            Assert.notNull(tokenResolver, "resolver cannot be null");
            this.bearerTokenResolver = tokenResolver;
        }

    }
}
